#include <fx.h>
#include <fx3d.h>
#include <fxkeys.h>
#include <list>
#include <string>

#include "core/Defs.h"
#include "utils/CommonFunctions.h"
#include "core/iException.h"
#include "core/Environment.h"
#include "gui/Events.h"
#include "gui/EventManager.h"
#include "gui/iGraphicsSubsystem.h"
#include "render/iRenderer.h"

#include "FOXterrier.h"
#include "FOXLogger.h"
#include "Defs.h"
#include "FOXGraphicsSubsystem.h"
#include "FOXRenderTarget.h"


#include <GL/gl.h>

#include "core/Core.h"

/*// fixme: other systems 
#ifdef WIN32
#include "platform/win32/Core.h"
#else
#include "platform/linux/linCore.h"
#endif*/

using neiderra::core::iException;
using neiderra::core::Environment;
using neiderra::gui::EventManager;
using std::list;
using std::string;

FXDEFMAP(FOXterrier) FOXterrierMap[]={
	//________Message_Type_____________________ID_______________________________Message_Handler_______
	FXMAPFUNC(SEL_PAINT,		FOXterrier::ID_CANVAS,				FOXterrier::onExpose),
	FXMAPFUNC(SEL_CONFIGURE,	FOXterrier::ID_CANVAS,				FOXterrier::onConfigure),
	FXMAPFUNC(SEL_UPDATE,		FOXterrier::ID_CANVAS,				FOXterrier::onExpose),
	FXMAPFUNC(SEL_LEFTBUTTONPRESS,	FOXterrier::ID_CANVAS,			FOXterrier::onCanvasLeftMouseClicked),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_MENU_HELP_ABOUT,		FOXterrier::onMenuHelpAbout),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_MENU_VIEW_CONSOLE,	FOXterrier::onMenuViewConsole),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_MENU_CORE_SHUTDOWN,	FOXterrier::onMenuCoreShutdown),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_MENU_CORE_SUSPEND,	FOXterrier::onMenuCoreSuspend),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_MENU_CORE_RESUME,	FOXterrier::onMenuCoreResume),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_MENU_CORE_POWERUP,	FOXterrier::onMenuCorePowerup),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_MENU_CORE_ENV_SENDCMD,	FOXterrier::onMenuCoreEnvSendCmd),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_MENU_OPTIONS_SAVESETTINGS,	FOXterrier::onMenuOptionsSaveSettingsCmd),
	FXMAPFUNC(SEL_TIMEOUT,		FOXterrier::ID_SELF_UPDATE,			FOXterrier::onSelfUpdate),
	FXMAPFUNC(SEL_COMMAND,		FOXterrier::ID_CONSOLE,				FOXterrier::onConsoleCmd),
	//FXMAPFUNC(SEL_ENTER,		FOXterrier::ID_LOG,					FOXterrier::onLogCmd),
	//FXMAPFUNC(SEL_KEYPRESS,		FOXterrier::ID_CONSOLE,				FOXterrier::onConsoleKeyPressed),
	FXMAPFUNC(SEL_SIGNAL,		FOXterrier::ID_INTERRUPT,			FOXterrier::onCleanUpAndQuit)
};

FXIMPLEMENT(FOXterrier,FXMainWindow, FOXterrierMap,ARRAYNUMBER(FOXterrierMap)) 

FOXterrier::FOXterrier(FXApp* app)
: FXMainWindow(app, "FOXterrier", NULL, NULL, DECOR_ALL, 0, 0, 800, 600, 0, 0  )
{
	initControls();
//	initMenu();
	
	initSubsystems();

	//_statusBar->getStatusLine()->setNormalText("Ready");
//	_glcanvas->disable();

	int width, height;
	width = getApp()->reg().readIntEntry("foxterrier", "width", 800);
	height = getApp()->reg().readIntEntry("foxterrier", "height", 600);
	this->resize(width, height);
}

FOXterrier::~FOXterrier()
{
	shutdownSubsystems();

	// save geometry
	getApp()->reg().writeIntEntry("foxterrier", "width", getWidth());
	getApp()->reg().writeIntEntry("foxterrier", "height", getHeight());
	getApp()->reg().writeIntEntry("console", "consoleHeight", _logFrame->getHeight());
	getApp()->reg().writeBoolEntry("console", "hidden", !_logFrame->shown());

	getApp()->reg().write();

	delete _fileMenu;
	delete _helpMenu;
	delete _viewMenu;
	delete _coreMenu;
		delete _coreEnvMenu;
	delete _optionsMenu;

	delete _glvisual;
}

void FOXterrier::create()
{
	FXMainWindow::create();
	show(PLACEMENT_SCREEN);
}

void FOXterrier::initSubsystems()
{
	using namespace neiderra::core;
	/*#ifdef WIN32
	_core = new win32Core();
#else
	_core = new linCore(); // :) linCoooore :)
	#endif*/
	_core = new Core();

	//fixme delete default logger
	FOXLogger* logger = new FOXLogger(_core);
	logger->setOutput(_logList);
	_core->setLogger(logger);

	// graphics
	FOXGraphicsSubsystem* graphicsSubsystem = new FOXGraphicsSubsystem(_core);
	FOXRenderTarget* target = new FOXRenderTarget(_glcanvas);
	// fixme: setTarget not init
	graphicsSubsystem->getRenderer()->init(target);
	_core->setGraphicsSubsystem(graphicsSubsystem);

	exportMembers();

	_core->getEnvironment()->executeMethod("version");
}

void FOXterrier::shutdownSubsystems()
{
	delete _core;
}

void FOXterrier::initControls()
{
	FXMenuBar* menuBar = new FXMenuBar(this, LAYOUT_SIDE_TOP | LAYOUT_FILL_X );
	// Separator
	//new FXHorizontalSeparator(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X|SEPARATOR_GROOVE);

	FXSplitter* splitter;
	FXComposite* contents;
	
	_statusBar = new FXStatusBar(this,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER);

	// fixme: stop updating glcanvas while moving splitter / resizing windows
	splitter=new FXSplitter(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y|SPLITTER_REVERSED|SPLITTER_VERTICAL);

	// Contents
	//_contents = new FXVerticalFrame(this,LAYOUT_SIDE_TOP|FRAME_NONE|LAYOUT_FILL_X|LAYOUT_FILL_Y|PACK_UNIFORM_HEIGHT);
	_workFrame = new FXHorizontalFrame(splitter,LAYOUT_SIDE_TOP|FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0, 0,0,0,0);

	_glvisual = new FXGLVisual(getApp(),VISUAL_DOUBLEBUFFER|VISUAL_STEREO);
	_glcanvas=new FXGLCanvas(_workFrame,_glvisual,this,ID_CANVAS,LAYOUT_FILL_X|LAYOUT_FILL_Y);

	_logFrame = new FXVerticalFrame(splitter,LAYOUT_SIDE_TOP|FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0, 0,0,0,0);
	FXHorizontalFrame* consoleFrame = new FXHorizontalFrame(_logFrame, LAYOUT_FILL_X, 0,0,0,0, 0,0,0,0);
	new FXLabel(consoleFrame, ">");
	_txtfConsole = new FXTextField(consoleFrame,80,this,ID_CONSOLE, LAYOUT_SIDE_TOP|LAYOUT_FILL_X);
	//_txtfConsole = new FXComboBox(consoleFrame, 80, this, ID_CONSOLE, LAYOUT_SIDE_TOP|LAYOUT_FILL_X);
	_logList = new FXList(_logFrame,this,ID_LOG,LIST_EXTENDEDSELECT|LAYOUT_FILL_X|LAYOUT_FILL_Y);

	_fileMenu = new FXMenuPane(this);
	new FXMenuTitle(menuBar,"&File",NULL, _fileMenu);	
	new FXMenuCommand(_fileMenu, "&Quit\tCtl-Q\tQuit the application",NULL, getApp(), FXApp::ID_QUIT);

	_viewMenu = new FXMenuPane(this);
	new FXMenuTitle(menuBar,"&View",NULL, _viewMenu);	
//	new FXMenuCommand(_viewMenu, "&Console", NULL, this, ID_MENU_VIEW_CONSOLE);
	new FXMenuCheck(_viewMenu,"Toggle &Console\tF3\tToggles console window",_logFrame,FXWindow::ID_TOGGLESHOWN);

	_coreMenu = new FXMenuPane(this);
	new FXMenuTitle(menuBar,"&Core",NULL, _coreMenu);	
	new FXMenuCommand(_coreMenu, "&Power up!\tF5\tEnable Power Supply", NULL, this, ID_MENU_CORE_POWERUP);
	new FXMenuCommand(_coreMenu, "&Shutdown!\tF10\tDisable Power Supply", NULL, this, ID_MENU_CORE_SHUTDOWN);
	new FXMenuCommand(_coreMenu, "S&uspend", NULL, this, ID_MENU_CORE_SUSPEND);
	new FXMenuCommand(_coreMenu, "&Resume", NULL, this, ID_MENU_CORE_RESUME);

		_coreEnvMenu = new FXMenuPane(this);
		new FXMenuCommand(_coreEnvMenu, "&Send Command...\tF2", NULL, this, ID_MENU_CORE_ENV_SENDCMD);
		new FXMenuCascade(_coreMenu, "&Environment", NULL, _coreEnvMenu);

	_optionsMenu = new FXMenuPane(this);
	new FXMenuTitle(menuBar,"&Options",NULL, _optionsMenu);	
	new FXMenuCommand(_optionsMenu, "&Save Settings", NULL, this, ID_MENU_OPTIONS_SAVESETTINGS);
	
	_helpMenu = new FXMenuPane(this);
	new FXMenuTitle(menuBar,"&Help",NULL, _helpMenu);	
	new FXMenuCommand(_helpMenu, "&About\tF1", NULL, this, ID_MENU_HELP_ABOUT);

	_txtfConsole->setFocus();

	// setup

	// logFrame
	_logFrame->setHeight(getApp()->reg().readIntEntry("foxterrier", "consoleHeight", 200));
	bool hidden = getApp()->reg().readBoolEntry("console", "hidden", true); 
	if(hidden) _logFrame->hide();
}

void FOXterrier::initMenu()
{

}

void FOXterrier::setStatusMsg(const string& s)
{
	_statusBar->getStatusLine()->setNormalText(FXString(s.c_str()));
}

long FOXterrier::onExpose(FXObject*,FXSelector,void*)
{
	return 1.0;
}

long FOXterrier::onSelfUpdate(FXObject*,FXSelector,void*)
{
	try{

		if(_core->isWorking())
		{
			// fixme: get time
			_core->step(0.01);

			_glcanvas->makeCurrent();

			// fixme: dt
			// fixme: repaint glcanvas only when it's needed
			// todo: 2 modes: 1 - game (always repainting), 2 - edit (no animations)
			_core->getGraphicsSubsystem()->getRenderer()->update(100);

			_glcanvas->makeNonCurrent();
		}

		getApp()->addTimeout(this, ID_SELF_UPDATE, 100);
	}
	catch(iException e)
	{
		FXMessageBox::error(getApp(), MBOX_OK, "Fatal error", e.getReport().c_str());
		_core->shutdown();
	}
	return 1;
}

// Widget has been resized
long FOXterrier::onConfigure(FXObject*,FXSelector,void*)
{
	if(_glcanvas->makeCurrent())
	{
		glViewport(0,0,_glcanvas->getWidth(),_glcanvas->getHeight());
		_glcanvas->makeNonCurrent();
    }
	return 1;
}

long FOXterrier::onMenuHelpAbout(FXObject*,FXSelector,void*)
{
	FXMessageBox::information(getApp(), MBOX_OK, "About...", 
		("FOXterrier, v. " + FXString(FOXTERRIER_VERSION) +  "\nGUI to Neiderra Hub, v. " + FXString(NEIDERRA_VERSION)).text());
	return 1;
}

long FOXterrier::onMenuViewConsole(FXObject*,FXSelector,void*)
{
	// todo: main try catch to catch such exceptions globally
	// show this message, show other messages (FATAL, SHIT, FUCK, etc...)
	FXMessageBox::information(getApp(), MBOX_OK, "Console...",
		"Not implemented yet!");
	return 1;
}

long FOXterrier::onMenuCorePowerup(FXObject*,FXSelector,void*)
{
	getCore()->powerup();
	_statusBar->getStatusLine()->setNormalText("Running...");
	getApp()->addTimeout(this, ID_SELF_UPDATE, 100);
	return 1;
}

long FOXterrier::onMenuCoreSuspend(FXObject*,FXSelector,void*)
{
	
	return 1;
}

long FOXterrier::onMenuCoreResume(FXObject*,FXSelector,void*)
{

	return 1;
}

long FOXterrier::onMenuCoreShutdown(FXObject*,FXSelector,void*)
{
	_statusBar->getStatusLine()->setNormalText("Shutdown...");
	getCore()->shutdown();
	getApp()->removeTimeout(this, ID_SELF_UPDATE);
	_statusBar->getStatusLine()->setNormalText("Ready");

	return 1;
}

long FOXterrier::onMenuCoreEnvSendCmd(FXObject*,FXSelector,void*)
{
	_txtfConsole->setText("");
	_txtfConsole->setFocus();
	return 1;
}

long FOXterrier::onMenuOptionsSaveSettingsCmd(FXObject*,FXSelector,void*)
{
	setStatusMsg("not implemented yet :)");
	return 1;
}

long FOXterrier::onConsoleCmd(FXObject*,FXSelector,void*)
{
	FXString cmdText = _txtfConsole->getText();
	
	std::list<string> tokens;
	FXint pos = 0, lastpos = 0;
	string cmd=cmdText.text();
	while(pos <= cmdText.length())
	{
        lastpos = pos;
		pos = cmdText.find(" ", pos+1);
		if(pos == -1)
		{
			pos = cmdText.length();
			tokens.push_back(cmdText.mid(lastpos, pos).text());
			break;
		}
		else if(lastpos != 0)
			tokens.push_back(cmdText.mid(lastpos, pos).text());
		else if(lastpos == 0)
		{
            cmd=cmdText.mid(lastpos, pos).text();
		}
	}
	
	_core->getEnvironment()->executeMethod(cmd,tokens);
	_txtfConsole->setText("");

	return 1;
}

long FOXterrier::onLogCmd(FXObject* o,FXSelector sel,void* ptr)
{
	FXListItem *item = _logList->getItem(_logList->getCurrentItem());
	if(item == 0)
		return 0;

	FXString cmd = item->getText();
	_txtfConsole->setText(cmd);
	onConsoleCmd(o, sel, ptr);
	return 1;
}

long FOXterrier::onCanvasLeftMouseClicked(FXObject*,FXSelector,void*)
{
	using neiderra::gui::events::GUIMouseEvent;

	GUIMouseEvent* e = new GUIMouseEvent();
	e->button_num = GUIMouseEvent::mb_left;
	// fixme
	e->type = GUIMouseEvent::mouse_click;
	e->pwin = 0;
	// fixme
	e->mx = 0;
	e->my = 0;
	_core->getGraphicsSubsystem()->getEventManager()->pushEvent(e);
	return 1;
}

long FOXterrier::onConsoleKeyPressed(FXObject* o,FXSelector sel,void* ptr)
{
	FXEvent* event = (FXEvent*)ptr;
	switch(event->code)
	{
	case KEY_Up:
		_core->getLogger()->log("up");
		break;
	case KEY_Down:
		_core->getLogger()->log("down");
		break;
	default:
		o->onDefault(this, sel, ptr);
	}
	return 1;
}

long FOXterrier::onCleanUpAndQuit(FXObject*,FXSelector,void*)
{
	_core->getLogger()->log("shutting down...");
	return 1;
}

//
// iEnvExporter implementation
//

void FOXterrier::executeMethod(const string& name, const list<string> &args)
{
	if(name == "help")
	{
		list<string> cmdList = _core->getEnvironment()->getCmdList();
		
		list<string>::iterator i = cmdList.begin();
		for(;i!=cmdList.end();i++)
		{
			string cmd = *i;
			_core->getLogger()->log(cmd);
		}
	}
	else if(name == "quit")
	{
		_core->shutdown();
		getApp()->exit();
	}
	else if(name == "version")
	{
		_core->getLogger()->log(string("Neiderra v. ") + NEIDERRA_VERSION);
		_core->getLogger()->log(string("Foxterrier v. ") + FOXTERRIER_VERSION);
	}
	else if(name == "clear")
	{
		_logList->clearItems();
	}
}

void FOXterrier::exportMembers()
{
	_core->getEnvironment()->registerMethod("help", this);
	_core->getEnvironment()->registerMethod("quit", this);
	_core->getEnvironment()->registerMethod("version", this);
	_core->getEnvironment()->registerMethod("clear", this);
}
